#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# pip install aliyun-python-sdk-core-v3 aliyun-python-sdk-alidns
"""
使用示例:
python aliyun_ddns.py \
--secret_id YOUR_ALIYUN_SECRET_ID \
--secret_key YOUR_ALIYUN_SECRET_KEY  \
--host ddns.wuyou.run --ip_url ipconfig.io
"""

import argparse
import re
import json
import requests
import os
import logging
from logging.handlers import RotatingFileHandler
from aliyunsdkcore.client import AcsClient
from aliyunsdkalidns.request.v20150109.DescribeSubDomainRecordsRequest import DescribeSubDomainRecordsRequest
from aliyunsdkalidns.request.v20150109.AddDomainRecordRequest import AddDomainRecordRequest
from aliyunsdkalidns.request.v20150109.UpdateDomainRecordRequest import UpdateDomainRecordRequest
from aliyunsdkcore.acs_exception.exceptions import ServerException

class AliyunDDNS:
    def __init__(self, secret_id, secret_key, host, ip_url):
        self.AK = secret_id
        self.AS = secret_key

        self.domian = '.'.join(host.split('.')[-2:])
        self.subdomain = host.split('.')[0]
        self.ip_url = ip_url
        self.logger = self.setup_logging()

    def getip(self):
        try:
            req = requests.get(self.ip_url, timeout=3)
            if req.status_code != requests.codes.ok:
                return None
        except Exception as e:
            return None

        return re.findall(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", req.text)[0]

    def describe_subdomain_records(self, client):
        # 查询子域名解析记录
        request = DescribeSubDomainRecordsRequest()
        request.set_accept_format('json')

        request.set_Type("A")
        request.set_SubDomain(self.subdomain + "." + self.domian)

        response = client.do_action_with_exception(request)
        response = str(response, encoding='utf-8')
        result = json.loads(response)
        return result

    def add_record(self, client, priority, ttl):
        # 添加解析记录
        request = AddDomainRecordRequest()
        request.set_accept_format('json')

        request.set_Priority(priority)
        request.set_TTL(ttl)
        request.set_Value(self.ipaddr)
        request.set_Type("A")
        request.set_RR(self.subdomain)
        request.set_DomainName(self.domian)
        # request.set_DomainName(self.subdomain)
        # request.set_DomainName(self.subdomain.split('.')[1])

        response = client.do_action_with_exception(request)
        response = str(response, encoding='utf-8')
        result = json.loads(response)
        return result

    def update_record(self, client, priority, ttl, record_id):
        # 更新解析记录
        request = UpdateDomainRecordRequest()
        request.set_accept_format('json')

        request.set_Priority(priority)
        request.set_TTL(ttl)
        request.set_Value(self.ipaddr)
        request.set_Type("A")
        request.set_RR(self.subdomain)
        request.set_RecordId(record_id)

        response = client.do_action_with_exception(request)
        response = str(response, encoding='utf-8')
        return response

    def setup_logging(self):
        log_dir = ''
        log_file = 'ddns.log'

        log_path = os.path.join(log_dir, log_file)
        log_formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s  (%(filename)s:%(lineno)d)')

        logger = logging.getLogger()
        logger.setLevel(logging.INFO)

        # 创建一个轮换的日志文件处理器
        handler = RotatingFileHandler(log_path, maxBytes=10*1024*1024, backupCount=3)
        handler.setFormatter(log_formatter)
        logger.addHandler(handler)

        return logger

    def run(self):
        self.ipaddr = self.getip()
        self.logger.info(f"获取ip:{self.ipaddr}  域名:{self.subdomain}.{self.domian}")
        
        if not self.ipaddr:
            self.logger.error("无法获取外部IP地址。")
            return

        client = AcsClient(self.AK, self.AS, "cn-hangzhou")

        try:
            des_result = self.describe_subdomain_records(client)

            if des_result.get("TotalCount", 0) == 0:
                add_result = self.add_record(client, "5", "600")
                self.logger.info("解析记录已成功添加。")
            else:
                existing_ip = des_result["DomainRecords"]["Record"][0]["Value"]
                if existing_ip != self.ipaddr:
                    record_id = des_result["DomainRecords"]["Record"][0]["RecordId"]
                    self.update_record(client, "5", "600", record_id)
                    self.logger.info("解析记录已成功更新。")
                else:
                    self.logger.info("解析记录与当前IP相同，无需更新。")
        except Exception as e:
            self.logger.error(f"发生错误: {str(e)}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Aliyun DDNS Script')
    parser.add_argument('--secret_id', required=True, help='Aliyun Access Key ID')
    parser.add_argument('--secret_key', required=True, help='Aliyun Access Key Secret')
    parser.add_argument('--host', required=True, help='域名主机')
    parser.add_argument('--ip_url', required=True, help='IP查询网址（例如，ipconfig.io）')

    args = parser.parse_args()

    ddns = AliyunDDNS(args.secret_id, args.secret_key, args.host, args.ip_url)
    ddns.run()
